#include "types.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>

using namespace std;

string itos(int x) {
    char *s = new char[65];
    sprintf(s, "%d", x);
    return s;
}

int stoi(string s) {
    return (int) strtol(s.c_str(), NULL, 10);
}

// Type member functions.

Type::Type(string name, Type* undertype, int dimension) {
    _name = name;
    _undertype = undertype;
    _dimension = dimension;
    _dimstr = itos(dimension);
}

Type::Type(string name, Type* undertype, string dimension) {
    _name = name;
    _undertype = undertype;
    _dimension = stoi(dimension);
    _dimstr = dimension;
}

string Type::name() {
    return _name;
}

string Type::cderef(string var, int index) {
    char buffer[256];
    sprintf(buffer, (this->operator [](index)).c_str(), var.c_str());
    return buffer;
}

string Type::cderef(string var, string index) {
    char buffer[256];
    sprintf(buffer, (this->operator [](index)).c_str(), var.c_str());
    return buffer;
}

string Type::dec(string var) {
    return var + " : " + _name;
}

string Type::dimstr() {
    return _dimstr;
}

int Type::dimension() {
    return _dimension;
}

Type* Type::undertype() {
    return _undertype;
}

bool Type::simple() {
    return !_undertype;
}

bool Type::complex() {
    return _undertype;
}

bool Type::equals(Type* othertype) {
    if (othertype)
        return name() == othertype->name();
    else
        return false;
}

bool Type::equals(Type& othertype) {
    return name() == othertype.name();
}

bool Type::receives(Type& t) {
    if (equals(t))
        return true;
    else
        return false;
}

bool Type::receives(Type* t) {
    if (equals(*t))
        return true;
    else
        return false;
}

Type* Type::operator +(Type& t) {
    return NULL;
}

Type* Type::operator -(Type& t) {
    return NULL;
}

Type* Type::operator -() {
    return NULL;
}

Type* Type::operator *(Type& t) {
    return NULL;
}

Type* Type::operator /(Type& t) {
    return NULL;
}

Type* Type::operator %(Type& t) {
    return NULL;
}

Type* Type::operator>(Type& t) {
    return NULL;
}

Type* Type::operator<(Type& t) {
    return NULL;
}

Type* Type::operator >=(Type& t) {
    return NULL;
}

Type* Type::operator <=(Type& t) {
    return NULL;
}

Type* Type::operator !() {
    return NULL;
}

Type* Type::operator &&(Type& t) {
    return NULL;
}

Type* Type::operator ||(Type& t) {
    return NULL;
}

Type* Type::operator *() {
    return _undertype;
}

string Type::operator[](int index) {
    if (_undertype)
        return "*(%s + " + itos(index) + ")";
    else
        return NULL;
}

string Type::operator[](string index) {
    if (_undertype)
        return "*(%s + " + index + ")";
    else
        return NULL;
}

// SimpleType member functions.

SimpleType::SimpleType(string name, string cname, int size) :
Type(name, NULL, 1) {
    _cname = cname;
    _size = size;
}

string SimpleType::tempsuffix() {
    string result = "_";
    result += _cname[0];
    return result;
}

string SimpleType::cname() {
    return _cname;
}

string SimpleType::cdec(string var) {
    return _cname + " " + var;
}

int SimpleType::size() {
    return _size;
}

// NumericType member functions.

NumericType::NumericType(string name, string cname, int size)
: SimpleType(name, cname, size) {
}

Type* NumericType::stronger(Type* t1, Type* t2) {
    if (t1->numeric() && t2->numeric())
        if (t1->character())
            return t2;
        else if (t2->character())
            return t1;
        else if (t1->natural())
            return t2;
        else if (t2->natural())
            return t1;
        else if (t1->integer())
            return t2;
        else if (t2->integer())
            return t1;
        else
            return t1;
    else
        return false;
}

Type* NumericType::stronger(Type* t1, Type& t2) {
    return stronger(t1, &t2);
}

Type* NumericType::stronger(Type& t1, Type* t2) {
    return stronger(&t1, t2);
}

Type* NumericType::stronger(Type& t1, Type& t2) {
    return stronger(&t1, &t2);
}

bool NumericType::numeric() {
    return true;
}

bool NumericType::logic() {
    return false;
}

bool NumericType::receives(Type& t) {
    if (stronger(this, t) == this) {
        return true;
    } else {
        return false;
    }
}

bool NumericType::receives(Type* t) {
    if (stronger(this, *t) == this) {
        return true;
    } else {
        return false;
    }
}

Type* NumericType::operator +(Type& t) {
    return stronger(this, t);
}

Type* NumericType::operator -(Type& t) {
    return stronger(this, t);
}

Type* NumericType::operator -() {
    return this;
}

Type* NumericType::operator *(Type& t) {
    return stronger(this, t);
}

Type* NumericType::operator /(Type& t) {
    return stronger(this, t);
}

Type* NumericType::operator %(Type& t) {
    return stronger(this, t);
}

Type* NumericType::operator>(Type& t) {
    if (t.numeric())
        return Logic::type();
    else
        return NULL;
}

Type* NumericType::operator<(Type& t) {
    if (t.numeric())
        return Logic::type();
    else
        return NULL;
}

Type* NumericType::operator >=(Type& t) {
    if (t.numeric())
        return Logic::type();
    else
        return NULL;
}

Type* NumericType::operator <=(Type& t) {
    if (t.numeric())
        return Logic::type();
    else
        return NULL;
}

// Real member functions.
Type* Real::_type;

Real::Real() : NumericType("real", "float", sizeof (float)) {
}

Real::Real(string name, string cname, int size)
: NumericType(name, cname, size) {
}

Type* Real::type() {
    if (!_type)
        _type = new Real();
    return _type;
}

bool Real::real() {
    return true;
}

bool Real::integer() {
    return false;
}

bool Real::natural() {
    return false;
}

bool Real::character() {
    return false;
}

// Int member functions.
Type* Int::_type;

Int::Int() : Real("int", "int", sizeof (int)) {
}

Int::Int(string name, string cname, int size)
: Real(name, cname, size) {
}

Type* Int::type() {
    if (!_type)
        _type = new Int();
    return _type;
}

bool Int::integer() {
    return true;
}

bool Int::natural() {
    return false;
}

bool Int::receives(Type& t) {
    return stronger(this, t) == this;
}

bool Int::receives(Type* t) {
    return stronger(this, *t) == this;
}

// Nat member functions.
Type* Nat::_type;

Nat::Nat() : Int("nat", "unsigned", sizeof (unsigned)) {
}

Type* Nat::type() {
    if (!_type)
        _type = new Nat();
    return _type;
}

bool Nat::natural() {
    return true;
}

Type* Nat::operator -(Type& t) {
    if (t.natural())
        return Int::type();
    else if (t.numeric())
        return &t;
    else return NULL;
}

Type* Nat::operator -() {
    return Int::type();
}

// Char member functions.
Type* Char::_type;

Char::Char() : Int("char", "char", sizeof (char)) {
}

Type* Char::type() {
    if (!_type)
        _type = new Char();
    return _type;
}

bool Char::character() {
    return true;
}

Type* Char::operator +(Type& t) {
    if (&t == _type)
        return _type;
    else if (&t == Int::type())
        return Int::type();
    else if (&t == Real::type())
        return Real::type();
    else return NULL;
}

bool Char::receives(Type& t) {
    if (t.integer())
        return true;
    else
        return false;
}

bool Char::receives(Type* t) {
    if (t->integer())
        return true;
    else
        return false;
}

// Logic member functions.
Type* Logic::_type;

Logic::Logic() : SimpleType("logic", "int", sizeof (bool)) {
}

Type* Logic::type() {
    if (!_type)
        _type = new Logic();
    return _type;
}

bool Logic::numeric() {
    return false;
}

bool Logic::real() {
    return false;
}

bool Logic::integer() {
    return false;
}

bool Logic::natural() {
    return false;
}

bool Logic::character() {
    return false;
}

bool Logic::logic() {
    return true;
}

string Logic::tempsuffix() {
    return "_b";
}

Type* Logic::operator !() {
    return Logic::type();
}

Type* Logic::operator &&(Type& t) {
    if (t.logic())
        return Logic::type();
    else return NULL;
}

Type* Logic::operator ||(Type& t) {
    if (t.logic())
        return Logic::type();
    else return NULL;
}

// ComplexType member functions.

ComplexType::ComplexType(string name, Type* undertype,
        string dimension) : Type(name, undertype, dimension) {
}

ComplexType::ComplexType(string name, Type* undertype,
        int dimension) : Type(name, undertype, dimension) {
}

string ComplexType::tempsuffix() {
    string result = "_";
    result += name()[0];
    result += undertype()->tempsuffix();
    return result;
}

string ComplexType::cname() {
    return cdec("");
}

string ComplexType::cdec(string var) {
    string dimensions = "[" + dimstr() + "]";
    Type* utype = undertype();
    while (!utype->simple()) {
        dimensions += "[" + utype->dimstr() + "]";
        utype = utype->undertype();
    }
    return utype->cname() + " " + var + dimensions;
}

int ComplexType::size() {
    return undertype()->size() * dimension();
}

bool ComplexType::numeric() {
    return false;
}

bool ComplexType::real() {
    return false;
}

bool ComplexType::integer() {
    return false;
}

bool ComplexType::natural() {
    return false;
}

bool ComplexType::character() {
    return false;
}

bool ComplexType::logic() {
    return false;
}

bool ComplexType::receives(Type &t) {
    Type *utype;
    if (t.simple())
        return false;
    else if (undertype()->equals(t.undertype()))
        utype = undertype();
    else if (t.undertype()->numeric())
        utype = NumericType::stronger(undertype(), t.undertype());
    else
        return false;
    if (utype == undertype() && dimension() == t.dimension())
        return true;
    else
        return false;
}

bool ComplexType::receives(Type *t) {
    Type *utype;
    if (t->simple())
        return false;
    else if (undertype()->equals(t->undertype()))
        utype = undertype();
    else if (t->undertype()->numeric())
        utype = NumericType::stronger(undertype(), t->undertype());
    else
        return false;
    if (utype == undertype() && dimension() == t->dimension())
        return true;
    else
        return false;
}

// Array member functions.

Array::Array(Type* undertype, int dimension)
: ComplexType("array(" + undertype->name()
+ ":" + itos(dimension) + ")", undertype, dimension) {
}

Array::Array(Type *undertype, string dimension)
: ComplexType("array(" + undertype->name()
+ ":" + dimension + ")", undertype, dimension) {
}

// Vector member functions.

Vector::Vector(Type* undertype, int dimension)
: ComplexType("vector(" + undertype->name()
+ ":" + itos(dimension) + ")", undertype, dimension) {
}

Vector::Vector(Type *undertype, string dimension)
: ComplexType("vector(" + undertype->name()
+ ":" + dimension + ")", undertype, dimension) {
}

Type* Vector::operator +(Type& t) {
    Type* utype = NumericType::stronger(undertype(), t.undertype());
    if (utype != NULL && dimension() == t.dimension())
        return new Vector(utype, dimension());
    else
        return NULL;
}

Type* Vector::operator -(Type& t) {
    Type* utype = NumericType::stronger(undertype(), t.undertype());
    if (utype != NULL && dimension() == t.dimension())
        return new Vector(utype, dimension());
    else
        return NULL;
}

Type* Vector::operator *(Type& t) {
    Type* utype;
    if (t.numeric())
        utype = NumericType::stronger(undertype(), t);
    else if (dimension() == t.dimension())
        utype = NumericType::stronger(undertype(), t.undertype());
    else
        utype = NULL;
    return utype;
}

// String member functions.

String::String(int dimension)
: ComplexType("string", Char::type(), dimension) {
}

String::String(string dimension)
: ComplexType("string", Char::type(), dimension) {
}

Type* String::operator +(Type& t) {
    if (equals(t))
        return new String(dimension() + t.dimension());
    else
        return NULL;
}

// Set member functions.

Set::Set(Type* undertype) : ComplexType("set", undertype, 2) {
}